package org.jsola.hr.provider.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.jsola.approval.constant.ApproType;
import org.jsola.approval.constant.ApprovalConstants;
import org.jsola.approval.constant.ProcessType;
import org.jsola.approval.dto.*;
import org.jsola.approval.entity.ApproFlowDO;
import org.jsola.approval.entity.ApproFlowGroupDO;
import org.jsola.approval.entity.ApproFlowNode;
import org.jsola.approval.entity.ApproFlowProcessDO;
import org.jsola.approval.query.ApproFlowQuery;
import org.jsola.approval.query.UserFlowApproQuery;
import org.jsola.approval.service.*;
import org.jsola.approval.vo.*;
import org.jsola.common.StrKit;
import org.jsola.core.entity.BaseDO;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.HrLogKit;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.entity.CompanyDO;
import org.jsola.hr.provider.IApproProviderService;
import org.jsola.hr.provider.IUserProviderService;
import org.jsola.hr.service.ICompanyService;
import org.jsola.hr.service.IDepInfoService;
import org.jsola.hr.service.IEmpInfoService;
import org.jsola.hr.service.IPositionInfoService;
import org.jsola.hr.vo.*;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import static org.jsola.approval.common.Constants.NODE_TYPE_APPROVER;
import static org.jsola.approval.common.Constants.NODE_TYPE_NOTIFIER;
import static org.jsola.hr.constant.HrConstants.*;

/**
 * @author zxy
 */
@Service("hrApproProviderService")
@Slf4j
public class ApproProviderServiceImpl implements IApproProviderService {

    @Autowired
    private IApprovalInitService approvalInitService;

    @Autowired
    private IApproFlowService approFlowService;

    @Autowired
    private IApproFlowFormService approFlowFormService;

    @Autowired
    private IUserApproService userApproService;

    @Autowired
    private IUserApproInfoService userApproInfoService;

    @Autowired
    private IDepInfoService depInfoService;

    @Autowired
    private IEmpInfoService empInfoService;

    @Autowired
    private IPositionInfoService positionInfoService;

    @Autowired
    private IUserProviderService userProviderService;

    @Autowired
    private IApproFlowGroupService flowGroupService;

    @Autowired
    private IApproFlowNotifierService flowNotifierService;

    @Autowired
    private IApproFlowStartService approFlowStartService;

    @Autowired
    private IApproFlowApproverService flowApproverService;

    @Autowired
    private IApproFlowProcessService flowProcessService;

    @Autowired
    private IApproFlowSettingService approFlowSettingService;

    @Autowired
    private ICompanyService companyService;

    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void initApproval(String companyId, TokenUser tokenUser) {
        approvalInitService.initApproval(companyId, tokenUser);
    }

    @Override
    public List<ApproFlowListVO> listOnJobCanUseFlowsByCompany(String companyId, TokenUser tokenUser) {
        return approFlowService.listCanUseFlowsByCompanyIdAndType("hr", companyId, ApproType.ON_JOB.getValue(), tokenUser);
    }

    @Override
    public List<ApproFlowListVO> listLeavingCanUseFlowsByCompany(String companyId, TokenUser tokenUser) {
        return approFlowService.listCanUseFlowsByCompanyIdAndType("hr", companyId, ApproType.LEAVING.getValue(), tokenUser);
    }

    @Override
    public List<ApproFlowListVO> listTransferDutyCanUseFlowsByCompany(String companyId, TokenUser tokenUser) {
        return approFlowService.listCanUseFlowsByCompanyIdAndType("hr", companyId, ApproType.TRANSFER_DUTY.getValue(), tokenUser);
    }

    @Override
    public List<ApproFlowListVO> listTurnStraightCanUseFlowsByCompany(String companyId, TokenUser tokenUser) {
        return approFlowService.listCanUseFlowsByCompanyIdAndType("hr", companyId, ApproType.TURN_STRAIGHT.getValue(), tokenUser);
    }

    @Override
    public List<ApproFlowListVO> listReimbursementCanUseFlowsByCompany(String companyId, TokenUser tokenUser) {
        return approFlowService.listCanUseFlowsByCompanyIdAndType("hr", companyId, ApproType.REIMBURSEMENT.getValue(), tokenUser);
    }

    @Override
    public List<ApproFlowListVO> listLeaveCanUseFlowsByCompany(String companyId, TokenUser tokenUser) {
        return approFlowService.listCanUseFlowsByCompanyIdAndType("hr", companyId, ApproType.LEAVE.getValue(), tokenUser);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public UserApproVO createOnJobAppro(EmpInfoVO empInfoVO, String approFlowId, TokenUser tokenUser) {
        // 根据员工信息和员工变更记录构建审批所需信息
        List<UserApproInfoDTO> userApproInfoList = new ArrayList<>();

        // 根据审批id拉取入职审批表单
        List<ApproFlowFormListVO> flowFormList = approFlowFormService.listAllFormsByFlowId(approFlowId, false, tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(flowFormList)) {
            throw new ParamException("流程id不存在：" + approFlowId);
        }
        flowFormList.forEach(
                flowForm -> {
                    UserApproInfoDTO userApproInfoDTO = new UserApproInfoDTO();
                    userApproInfoDTO.setFormId(flowForm.getId());
                    switch (flowForm.getCode()) {
                        case "userName":
                        case "applyUser":
                            // 入职员工姓名
                            userApproInfoDTO.setValue(Objects.isNull(empInfoVO) ? "" : empInfoVO.getId());
                            break;
                        case "department":
                            // 用人部门
                            userApproInfoDTO.setValue(empInfoVO.getPreJoinDep());
                            break;
                        case "position":
                            // 职位
                            userApproInfoDTO.setValue(empInfoVO.getPreJoinPosition());
                            break;
                        case "phone":
                            // 职工手机号
                            userApproInfoDTO.setValue(Objects.isNull(empInfoVO) ? "" : empInfoVO.getPhone());
                            break;
                        case "entryTime":
                            // 入职日期
                            if (Objects.isNull(empInfoVO) || empInfoVO.getPreJoinDate() == null) {
                                userApproInfoDTO.setValue("");
                                break;
                            }
                            userApproInfoDTO.setValue(formatter.format(empInfoVO.getPreJoinDate()));
                            break;
                        default:
                            break;
                    }
                    userApproInfoList.add(userApproInfoDTO);
                }
        );
        String subjectId = empInfoVO.getId();
        UserApproVO userApproVO = addUserAppro(empInfoVO.getName() + "的入职审批", approFlowId, userApproInfoList, SUBJECT_TYPE_HR_ON_JOB, subjectId, tokenUser);
        // 新增用户审批
        HrLogKit.logForSave("新增员工入职审批,empName:" + empInfoVO.getName() + ",empId:" + empInfoVO.getId(), "onJobAppro", userApproVO.getId(), tokenUser);

        return userApproVO;
    }

    /**
     * 发起审批
     *
     * @param title             审批标题
     * @param approFlowId       流程id
     * @param userApproInfoList 表单信息
     * @param subjectType       业务类型
     * @param subjectId         业务id
     * @param tokenUser         当前用户
     * @return 审批信息
     */
    private UserApproVO addUserAppro(String title, String approFlowId, List<UserApproInfoDTO> userApproInfoList, String subjectType, String subjectId, TokenUser tokenUser) {
        NewApproDTO newApproDTO = new NewApproDTO();
        newApproDTO.setTitle(title);
        newApproDTO.setFlowId(approFlowId);
        newApproDTO.setSubjectId(subjectId);
        newApproDTO.setSubjectType(subjectType);
        newApproDTO.setUserApproInfoList(userApproInfoList);
        return userApproService.addUserAppro(newApproDTO, tokenUser);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public UserApproVO createTurnStraightAppro(EmpInfoVO empInfoVO, EmpChangesVO empChangesVO, String approFlowId, TokenUser tokenUser) {
        // 根据员工信息和员工变更记录构建审批所需信息
        List<UserApproInfoDTO> userApproInfoList = new ArrayList<>();

        // 根据审批id拉取入职审批表单
        List<ApproFlowFormListVO> flowFormList = approFlowFormService.listAllFormsByFlowId(approFlowId, false, tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(flowFormList)) {
            return null;
        }
        flowFormList.forEach(
                flowForm -> {
                    UserApproInfoDTO userApproInfoDTO = new UserApproInfoDTO();
                    userApproInfoDTO.setFormId(flowForm.getId());
                    switch (flowForm.getCode()) {
                        case "userName":
                        case "applyUser":
                            // 入职员工姓名
                            userApproInfoDTO.setValue(Objects.isNull(empInfoVO) ? "" : empInfoVO.getId());
                            break;
                        case "department":
                            // 用人部门
                            userApproInfoDTO.setValue(StringUtils.isEmpty(empChangesVO.getCurrentDep()) ? "" : empChangesVO.getCurrentDep());
                            break;
                        case "position":
                            // 职位
                            userApproInfoDTO.setValue(StringUtils.isEmpty(empChangesVO.getCurrentPosition()) ? "" : empChangesVO.getCurrentPosition());
                            break;
                        case "phone":
                            // 职工手机号
                            userApproInfoDTO.setValue(Objects.isNull(empInfoVO) ? "" : empInfoVO.getPhone());
                            break;
                        case "entryTime":
                            // 入职日期
                            if (Objects.isNull(empInfoVO) || empInfoVO.getJoinDate() == null) {
                                userApproInfoDTO.setValue("");
                                break;
                            }
                            userApproInfoDTO.setValue(formatter.format(empInfoVO.getJoinDate()));
                            break;
                        case "formalTime":
                            // 转正日期
                            if (Objects.isNull(empChangesVO) || empChangesVO.getRegularDate() == null) {
                                userApproInfoDTO.setValue("");
                                break;
                            }
                            userApproInfoDTO.setValue(formatter.format(empChangesVO.getRegularDate()));
                            break;
                        default:
                            break;
                    }
                    userApproInfoList.add(userApproInfoDTO);
                }
        );

        // 新增用户审批
        return addUserAppro(empInfoVO.getName() + "的转正审批", approFlowId, userApproInfoList, SUBJECT_TYPE_HR_TURN_STRAIGHT, empInfoVO.getId(), tokenUser);
    }

    @Override
    public UserApproVO createTransferDutyAppro(EmpInfoVO empInfoVO, EmpChangesVO empChangesVO, String approFlowId, TokenUser tokenUser) {
        // 根据员工信息和员工变更记录构建审批所需信息
        List<UserApproInfoDTO> userApproInfoList = new ArrayList<>();

        // 根据审批id拉取入职审批表单
        List<ApproFlowFormListVO> flowFormList = approFlowFormService.listAllFormsByFlowId(approFlowId, false, tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(flowFormList)) {
            return null;
        }
        // 要填写的信息
        flowFormList.forEach(
                flowForm -> {
                    UserApproInfoDTO userApproInfoDTO = new UserApproInfoDTO();
                    userApproInfoDTO.setFormId(flowForm.getId());
                    switch (flowForm.getCode()) {
                        case "applyUser":
                            // 入职员工姓名
                            userApproInfoDTO.setValue(Objects.isNull(empInfoVO) ? "" : empInfoVO.getId());
                            break;
                        case "phone":
                            // 职工手机号
                            userApproInfoDTO.setValue(Objects.isNull(empInfoVO) ? "" : empInfoVO.getPhone());
                            break;

                        case "entryTime":
                            // 入职日期
                            if (Objects.isNull(empInfoVO) || empInfoVO.getJoinDate() == null) {
                                userApproInfoDTO.setValue("");
                                break;
                            }
                            userApproInfoDTO.setValue(formatter.format(empInfoVO.getJoinDate()));
                            break;
                        case "formalTime":
                            // 转正日期
                            if (Objects.isNull(empInfoVO) || empInfoVO.getTurnStraightDate() == null) {
                                userApproInfoDTO.setValue("");
                                break;
                            }
                            userApproInfoDTO.setValue(formatter.format(empInfoVO.getTurnStraightDate()));
                            break;
                        case "oldDepartment":
                            // 原部门
                            if (Objects.isNull(empChangesVO)) {
                                userApproInfoDTO.setValue("");
                            } else {
                                userApproInfoDTO.setValue(empChangesVO.getPreDep());
                            }
                            break;
                        case "oldPosition":
                            // 原职位
                            if (Objects.isNull(empChangesVO)) {
                                userApproInfoDTO.setValue("");
                            } else {
                                userApproInfoDTO.setValue(empChangesVO.getPrePosition());
                            }
                            break;
                        case "newDepartment":
                            // 转入部门
                            if (Objects.isNull(empChangesVO)) {
                                userApproInfoDTO.setValue("");
                            } else {
                                userApproInfoDTO.setValue(empChangesVO.getCurrentDep());
                            }
                            break;
                        case "newPosition":
                            // 转入职位
                            if (Objects.isNull(empChangesVO)) {
                                userApproInfoDTO.setValue("");
                            } else {
                                userApproInfoDTO.setValue(empChangesVO.getCurrentPosition());
                            }
                            break;
                        case "takeEffectTime":
                            // 调岗生效日期
                            if (Objects.isNull(empChangesVO) || empChangesVO.getAdjustmentDate() == null) {
                                userApproInfoDTO.setValue("");
                                break;
                            }
                            userApproInfoDTO.setValue(formatter.format(empChangesVO.getAdjustmentDate()));
                            break;
                        case "reason":
                            // 调岗原因/离职原因
                            userApproInfoDTO.setValue(Objects.isNull(empChangesVO) ? "" : empChangesVO.getAdjustmentReason());
                            break;
                        default:
                            break;
                    }
                    userApproInfoList.add(userApproInfoDTO);
                }
        );

        // 新增用户审批
        String subjectId = empInfoVO.getId();
        return addUserAppro(empInfoVO.getName() + "的调岗审批", approFlowId, userApproInfoList, SUBJECT_TYPE_HR_TRANSFER_DUTY, subjectId, tokenUser);
    }

    @Override
    public UserApproVO createLeavingAppro(EmpInfoVO empInfoVO, EmpChangesVO empChangesVO, String approFlowId, TokenUser tokenUser) {
        // 根据员工信息和员工变更记录构建审批所需信息
        List<UserApproInfoDTO> userApproInfoList = new ArrayList<>();

        // 根据审批id拉取入职审批表单
        List<ApproFlowFormListVO> flowFormList = approFlowFormService.listAllFormsByFlowId(approFlowId, false, tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(flowFormList)) {
            return null;
        }
        // 要填写的信息
        flowFormList.forEach(
                flowForm -> {
                    UserApproInfoDTO userApproInfoDTO = new UserApproInfoDTO();
                    userApproInfoDTO.setFormId(flowForm.getId());
                    switch (flowForm.getCode()) {
                        case "applyUser":
                            // 入职员工姓名
                            userApproInfoDTO.setValue(Objects.isNull(empInfoVO) ? "" : empInfoVO.getId());
                            break;
                        case "phone":
                            // 职工手机号
                            userApproInfoDTO.setValue(Objects.isNull(empInfoVO) ? "" : empInfoVO.getPhone());
                            break;
                        case "reason":
                            // 调岗原因/离职原因
                            userApproInfoDTO.setValue(Objects.isNull(empChangesVO) ? "" : empChangesVO.getAdjustmentReason());
                            break;
                        case "quitTime":
                            // 预计离职日期
                            if (Objects.isNull(empChangesVO) || empChangesVO.getPreQuitDate() == null) {
                                userApproInfoDTO.setValue("");
                                break;
                            }
                            userApproInfoDTO.setValue(formatter.format(empChangesVO.getPreQuitDate()));
                            break;
                        case "remarks":
                            // 离职/备注信息
                            userApproInfoDTO.setValue(Objects.isNull(empChangesVO) ? "" : empChangesVO.getQuitRemarks());
                            break;
                        default:
                            break;
                    }
                    userApproInfoList.add(userApproInfoDTO);
                }
        );
        // 新增用户审批
        String subjectId = empInfoVO.getId();
        return addUserAppro(empInfoVO.getName() + "的离职审批", approFlowId, userApproInfoList, SUBJECT_TYPE_HR_LEAVING, subjectId, tokenUser);
    }

    @Override
    public UserApproInfoVO selectUserApproInfoByFormId(String userApproId, String approFlowFormFormId, String siteId) {
        return userApproInfoService.selectByFormId(userApproId, approFlowFormFormId, siteId);
    }

    @Override
    public UserApproAllInfoVO findUserApproDetailsById(String userApproId, TokenUser tokenUser) {
        UserApproAllInfoVO approAllInfoVO = userApproService.findUserApproAndFormById(userApproId, tokenUser);
        if (!CollectionUtils.isEmpty(approAllInfoVO.getUserApproInfoList())) {
            approAllInfoVO.getUserApproInfoList().forEach(
                    userApproInfoListVO -> {
                        if (!StringUtils.isEmpty(userApproInfoListVO.getValue())) {
                            JSONObject props = JSONObject.parseObject(userApproInfoListVO.getProps());
                            if (props != null && props.get("type") != null) {
                                String type = props.getString("type");
                                String[] ids = userApproInfoListVO.getValue().split(",");
                                Map<String, String> valueMap = new HashMap<>();
                                if ("employee".equals(type)) {
                                    // 员工
                                    for (String id : ids) {
                                        EmpInfoVO empInfoVO = empInfoService.selectById(id, tokenUser.getSiteId());
                                        if (!Objects.isNull(empInfoVO)) {
                                            valueMap.put(id, empInfoVO.getName());
                                        }
                                    }
                                    userApproInfoListVO.setValueMap(valueMap);
                                } else if ("position".equals(type)) {
                                    // 岗位
                                    for (String id : ids) {
                                        PositionInfoVO positionInfoVO = positionInfoService.selectById(id, tokenUser.getSiteId());
                                        if (!Objects.isNull(positionInfoVO)) {
                                            valueMap.put(id, positionInfoVO.getName());
                                        }
                                    }
                                    userApproInfoListVO.setValueMap(valueMap);
                                } else if ("department".equals(type)) {
                                    // 部门
                                    for (String id : ids) {
                                        DepInfoVO depInfoVO = depInfoService.selectById(id, tokenUser.getSiteId());
                                        if (!Objects.isNull(depInfoVO)) {
                                            valueMap.put(id, depInfoVO.getName());
                                        }
                                    }
                                    userApproInfoListVO.setValueMap(valueMap);
                                }
                            }
                        }
                    }
            );
        }
        return approAllInfoVO;
    }

    @Override
    public EmpAndApproInfoVO findEmpApproDetailsBySubject(String subjectType, String subjectId, TokenUser tokenUser) {
        EmpInfoVO empInfoVO = empInfoService.selectById(subjectId, tokenUser.getSiteId());
        ParamException.notNull(empInfoVO, "员工信息不存在或已删除");
        EmpAndApproInfoVO empAndApproInfoVO = empInfoVO.to(EmpAndApproInfoVO.class);

        // 根据业务类型和业务id查询用户审批申请
        List<UserApproListVO> userApproList = userApproService.listBySubject(subjectType, subjectId, tokenUser.getSiteId());
        ParamException.notEmpty(userApproList, "该员工暂不存在该类型的审批申请");

        // 暂先返回最新的5条
        List<UserApproAllInfoVO> userApproAllInfoList = new ArrayList<>();
        for (UserApproListVO userApproListVO : userApproList) {
            UserApproAllInfoVO approAllInfoVO = findUserApproDetailsById(userApproListVO.getId(), tokenUser);
            userApproAllInfoList.add(approAllInfoVO);
        }
        empAndApproInfoVO.setUserApproAllInfoList(userApproAllInfoList);
        return empAndApproInfoVO;
    }

    @Override
    public List<ApproFlowNode> getFlowProcessByApproInfo(UserApproAddDTO userApproAddDTO, TokenUser tokenUser) {
        // 审批人为全员 暂时除去
//        if (CollectionUtils.isEmpty(flowProcessByApproInfo)) {
//            return new ArrayList<>();
//        }
//        flowProcessByApproInfo.parallelStream().forEach(approFlowNode -> {
//            // 审批人为全员时 返回人员列表为全员
//            if (org.apache.commons.lang3.StringUtils.isNotBlank(approFlowNode.getProps())) {
//                JSONObject jsonObject = (JSONObject) JSONObject.parse(approFlowNode.getProps());
//                if ("allUser".equals(jsonObject.get("range"))) {
//                    List<HrUserVO> hrUserVOList = userProviderService.listEmpAndRoleUserInCompany(new UserQuery()
//                            , userApproAddDTO.getCompanyId(), tokenUser.getSiteId());
//                    if (!CollectionUtils.isEmpty(hrUserVOList)) {
//                        List<Map<String, Object>> mapList = hrUserVOList.parallelStream().map(hrUserVO -> {
//                            Map<String, Object> map = new HashMap<>();
//                            map.put("id", hrUserVO.getId());
//                            map.put("isSelected", true);
//                            map.put("name", hrUserVO.getName());
//                            return map;
//                        }).collect(Collectors.toList());
//                        jsonObject.put("userList", mapList);
//                        approFlowNode.setProps(jsonObject.toJSONString());
//                    }
//                }
//            }
//        });
        return userApproService.getFlowProcessByApproInfo(userApproAddDTO, tokenUser);
    }

    @Override
    public List<ApproFlowListVO> listCanUseFlowsByType(String moduleType, String type, TokenUser tokenUser) {
        return approFlowService.listCanUseFlowsByType(moduleType, type, tokenUser);
    }

    @Override
    public String getFlowId(String type, TokenUser tokenUser) {
        List<ApproFlowListVO> flowList = this.listCanUseFlowsByType(HR_SUBJECT_TYPE, type, tokenUser);
        if (CollectionUtils.isEmpty(flowList)) {
            log.error("没有找到审批流程，请联系管理员,type:{}", type);
            return null;
        }
        return flowList.get(0).getId();
    }

    @Override
    public String getFlowIdByCompanyId(String type, String companyId, TokenUser tokenUser) {
        List<ApproFlowListVO> flowList = this.listCanUseFlowsByType(HR_SUBJECT_TYPE, type, tokenUser);
        if (CollectionUtils.isEmpty(flowList)) {
            log.error("没有找到审批流程，请联系管理员,companyId:{},type:{}", companyId, type);
            return null;
        }
        for (ApproFlowListVO approFlowListVO : flowList) {
            if (companyId.equals(approFlowListVO.getCompanyId())) {
                return approFlowListVO.getId();
            }
        }
        return flowList.get(0).getId();
    }

    @Override
    public boolean checkFlowId(String type, String companyId, String flowTemplateId, TokenUser tokenUser) {
        ParamException.hasText(companyId, "用工单位id不能为空");
        ParamException.hasText(flowTemplateId, "审批模板id不能为空");
        List<ApproFlowListVO> flowList = this.listCanUseFlowsByType(HR_SUBJECT_TYPE, type, tokenUser);
        if (CollectionUtils.isEmpty(flowList)) {
            log.error("没有找到审批流程，请联系管理员,companyId:{},type:{}", companyId, type);
            return false;
        }
        for (ApproFlowListVO approFlowListVO : flowList) {
            if (companyId.equals(approFlowListVO.getCompanyId()) && flowTemplateId.equals(approFlowListVO.getId())) {
                return true;
            }
        }
        return false;
    }


    @Override
    public UserApproVO addUserAppro(NewApproDTO newApproDTO, TokenUser tokenUser) {
        return userApproService.addUserAppro(newApproDTO, tokenUser);
    }

    @Override
    public List<UserApproListVO> listBySubject(String subjectType, String subjectId, TokenUser tokenUser) {
        if (StrKit.isEmpty(subjectType) || StrKit.isEmpty(subjectId)) {
            return new ArrayList<>();
        }
        return userApproService.listBySubject(subjectType, subjectId, tokenUser.getSiteId());
    }

    @Override
    public UserApproListVO selectLastBySubject(String subjectType, String subjectId, TokenUser tokenUser) {
        List<UserApproListVO> userApproListVOS = listBySubject(subjectType, subjectId, tokenUser);
        if (CollectionUtils.isEmpty(userApproListVOS)) {
            return null;
        }
        return userApproListVOS.get(0);
    }

    @Override
    @Transactional(value = ApprovalConstants.TX, rollbackFor = Exception.class)
    public JSONObject publishFlow(String flowInfo, TokenUser tokenUser) {
        // 校验流程
        JSONObject jsonObject = JSON.parseObject(flowInfo);

        ApproFlowDO approFlowDO = JSON.toJavaObject(jsonObject, ApproFlowDO.class);
        // 如果是新增才校验
        if (StrKit.isEmpty(approFlowDO.getId())) {
            ApproFlowGroupDO flowGroupDO = flowGroupService.selectDOById(approFlowDO.getGroupId(), tokenUser.getSiteId());
            String companyId = flowGroupDO.getCompanyId();
            String type = approFlowDO.getType();
            if (ApproType.ON_JOB.getValue().equals(type)
                    || ApproType.TURN_STRAIGHT.getValue().equals(type)
                    || ApproType.TRANSFER_DUTY.getValue().equals(type)
                    || ApproType.LEAVING.getValue().equals(type)) {

                ApproFlowQuery flowQuery = new ApproFlowQuery();
                flowQuery.setCompanyId(companyId);
                flowQuery.setType(type);
                flowQuery.setModuleType(HR_SUBJECT_TYPE);
                List<ApproFlowListVO> flowListVOList = approFlowService.select(flowQuery, tokenUser.getSiteId());
                String collect = flowListVOList.stream()
                        .map(ApproFlowListVO::getTypeName)
                        .collect(Collectors.joining(","));
                if (!CollectionUtils.isEmpty(flowListVOList) && !"0".equals(companyId)) {
                    throw new ParamException("本用工单位下已经有" + ApproType.find(type).getDesc() + "模板:" + collect);
                }
            }
        }

        return approFlowService.publishFlow(flowInfo, tokenUser);
    }

    @Override
    public List<ApproFlowListVO> selectByCompanyId(String companyId, String type, String siteId) {
        ApproFlowQuery flowQuery = new ApproFlowQuery();
        flowQuery.setModuleType(HR_SUBJECT_TYPE);
        flowQuery.setCompanyId(companyId);
        flowQuery.setType(type);
        return approFlowService.select(flowQuery, siteId);
    }

    @Override
    @Transactional(value = ApprovalConstants.TX, rollbackFor = Exception.class)
    public List<ApproFlowVO> saveIssuedApproFlow(IssuedApproFlowAddDTO issuedApproFlowAddDTO, TokenUser tokenUser) {
        List<IssuedApproFlowAddDTO.ApproFlowInfor> approFlowInforList = issuedApproFlowAddDTO.getApproFlowInforList();
        ParamException.notEmpty(approFlowInforList, "下发审批列表不能为空");
        List<String> companyIds = issuedApproFlowAddDTO.getCompanyIds().parallelStream().distinct().collect(Collectors.toList());

        // 查询下发的审批信息
        List<String> approFlowIds = approFlowInforList.parallelStream()
                .map(IssuedApproFlowAddDTO.ApproFlowInfor::getApproFlowId).collect(Collectors.toList());
        List<ApproFlowDO> approFlowDOList = approFlowService.selectDOByIds(approFlowIds, tokenUser);
        // 入转调离单一不能重复
        String[] strings = new String[]{ApproType.ON_JOB.getValue(), ApproType.TURN_STRAIGHT.getValue()
                , ApproType.TRANSFER_DUTY.getValue(), ApproType.LEAVING.getValue()};
        Map<String, List<ApproFlowDO>> typeListMap = approFlowDOList.parallelStream()
                .collect(Collectors.groupingBy(ApproFlowDO::getType));
        for (String type : strings) {
            List<ApproFlowDO> flowDOList = typeListMap.get(type);
            ParamException.isFalse((!CollectionUtils.isEmpty(flowDOList) && flowDOList.size() > 1)
                    , "同一类型审批只能下发一个");
        }
        List<String> types = typeListMap.keySet().parallelStream()
                .filter(type -> Arrays.asList(strings).contains(type)).distinct().collect(Collectors.toList());
        List<ApproFlowDO> flowDOList = approFlowService.selectByCompanyIdAndType(companyIds, types);

        List<CompanyDO> companyDOList = companyService.selectByIds(companyIds, tokenUser.getSiteId());
        // 用工单位id-name
        Map<String, String> companyIdNameMap = companyDOList.parallelStream()
                .collect(Collectors.toMap(BaseDO::getId, CompanyDO::getName));
        ParamException.isTrue(CollectionUtils.isEmpty(flowDOList), flowDOList.parallelStream().map(approFlowDO
                -> "用工单位：" + companyIdNameMap.get(approFlowDO.getCompanyId()) + "下已经有模板类型："
                + ApproType.find(approFlowDO.getType()).getDesc()).distinct()
                .collect(Collectors.joining(",", "[", "]")));
        // 查询公司下已存在的同名审批 有同名则不允许创建
        List<String> typeNameList = approFlowDOList.parallelStream().map(ApproFlowDO::getTypeName).collect(Collectors.toList());
        List<ApproFlowDO> hadApproFlowList = approFlowService.selectByCompanyIdAndTypeName(companyIds, typeNameList);
        List<String> issueApproFlowId = new ArrayList<>();
        // 公司下已存在同名审批
        for (ApproFlowDO approFlowDO : hadApproFlowList) {
            // 删除前先校验是否有人使用
            approFlowService.checkFlowBeUserNow(approFlowDO.getId(), tokenUser.getSiteId());
            // 删除下发的同名审批 重新下发
            issueApproFlowId.add(approFlowDO.getId());
        }
        approFlowService.deleteByIds(tokenUser, issueApproFlowId.toArray(new String[0]));
        List<String> groupNameList = approFlowInforList.parallelStream()
                .map(IssuedApproFlowAddDTO.ApproFlowInfor::getGroupName).collect(Collectors.toList());
        // 待下发Id-分组名称
        Map<String, String> approFlowIdGroupNameMap = approFlowInforList
                .parallelStream().collect(Collectors.toMap(IssuedApproFlowAddDTO.ApproFlowInfor::getApproFlowId
                        , IssuedApproFlowAddDTO.ApproFlowInfor::getGroupName));
        List<ApproFlowVO> approFlowVOList = new ArrayList<>();
        for (String companyId : companyIds) {
            // 查询公司下是否存在同名分组 不存在 需要创建
            List<ApproFlowGroupDO> approFlowGroupDOList =
                    flowGroupService.saveGroupNameIfNot(companyId, groupNameList, tokenUser);
            // 公司下分组名称-分组Id
            Map<String, String> groupNameIdMap = approFlowGroupDOList.parallelStream()
                    .collect(Collectors.toMap(ApproFlowGroupDO::getGroupName, BaseDO::getId));
            for (ApproFlowDO approFlowDO : approFlowDOList) {
                String flowId = approFlowDO.getId();
                String groupName = approFlowIdGroupNameMap.get(flowId);
                String groupId = groupNameIdMap.get(groupName);
                if (groupId == null) {
                    continue;
                }
                ApproFlowAddDTO approFlowAddDTO = approFlowDO.to(ApproFlowAddDTO.class);
                approFlowAddDTO.setCompanyId(companyId);
                approFlowAddDTO.setGroupId(groupId);
                // 设置下发状态为是
                approFlowAddDTO.setIssue(true);
                approFlowAddDTO.setSourceId(approFlowDO.getId());
                ApproFlowVO approFlowVO = approFlowService.save(approFlowAddDTO, tokenUser);
                approFlowVOList.add(approFlowVO);
                // 审批表单下发
                String siteId = tokenUser.getSiteId();
                List<ApproFlowFormListVO> approFlowFormListVOList = approFlowFormService.listAllFormsByFlowId(
                        flowId, false, siteId);
                if (!CollectionUtils.isEmpty(approFlowFormListVOList)) {
                    for (ApproFlowFormListVO approFlowFormListVO : approFlowFormListVOList) {
                        ApproFlowFormAddDTO approFlowFormAddDTO = approFlowFormListVO.to(ApproFlowFormAddDTO.class);
                        approFlowFormAddDTO.setFlowId(approFlowVO.getId());
                        approFlowFormService.save(approFlowFormAddDTO, tokenUser);
                    }
                }
                // 流程节点旧-新
                Map<String, String> nodeMap = new HashMap<>();
                // 流程起始节点下发
                ApproFlowStartVO approFlowStartVO = approFlowStartService.findFlowStartByFlowId(flowId, siteId);
                ParamException.notNull(approFlowStartVO, "未查询到流程设计中的起始节点，请查证后重试");
                ApproFlowStartAddDTO approFlowStartAddDTO = approFlowStartVO.to(ApproFlowStartAddDTO.class);
                approFlowStartAddDTO.setFlowId(approFlowVO.getId());
                ApproFlowStartVO save = approFlowStartService.save(approFlowStartAddDTO, tokenUser);
                nodeMap.put(approFlowStartVO.getId(), save.getId());
                // 非叶子节点
                boolean notLastNode = true;
                // 父节点Id
                String parentNodeId = approFlowStartVO.getId();
                // 新的父节点Id
                String newParentNodeId = save.getId();
                // 子节点类型
                String childNodeType = approFlowStartVO.getChildNodeType();
                while (notLastNode) {
                    switch (childNodeType) {
                        case NODE_TYPE_APPROVER:
                            // 获取流程审批人节点
                            ApproFlowApproverVO approFlowApprover = flowApproverService
                                    .findFlowApproverByParentId(flowId, parentNodeId, siteId);
                            if (approFlowApprover == null) {
                                notLastNode = false;
                                break;
                            }
                            // 保存本节点
                            ApproFlowApproverAddDTO approFlowApproverAddDTO = approFlowApprover.to(ApproFlowApproverAddDTO.class);
                            approFlowApproverAddDTO.setFlowId(approFlowVO.getId());
                            approFlowApproverAddDTO.setParentNodeId(newParentNodeId);
                            ApproFlowApproverVO approFlowApproverVO = flowApproverService.save(approFlowApproverAddDTO, tokenUser);
                            nodeMap.put(approFlowApprover.getId(), approFlowApproverVO.getId());
                            // 查询下个节点
                            notLastNode = !approFlowApprover.getIsLastNode();
                            childNodeType = approFlowApprover.getChildNodeType();
                            parentNodeId = approFlowApprover.getId();
                            newParentNodeId = approFlowApproverVO.getId();
                            break;
                        case NODE_TYPE_NOTIFIER:
                            // 获取流程抄送人节点
                            ApproFlowNotifierVO approFlowNotifierVO = flowNotifierService
                                    .findFlowNotifierByParentId(flowId, parentNodeId, siteId);
                            if (approFlowNotifierVO == null) {
                                notLastNode = false;
                                break;
                            }
                            ApproFlowNotifierAddDTO approFlowNotifierAddDTO = approFlowNotifierVO.to(ApproFlowNotifierAddDTO.class);
                            approFlowNotifierAddDTO.setFlowId(approFlowVO.getId());
                            approFlowNotifierAddDTO.setParentNodeId(newParentNodeId);
                            ApproFlowNotifierVO flowNotifierVO = flowNotifierService.save(approFlowNotifierAddDTO, tokenUser);
                            nodeMap.put(approFlowNotifierVO.getId(), flowNotifierVO.getId());
                            notLastNode = !approFlowNotifierVO.getIsLastNode();
                            childNodeType = approFlowNotifierVO.getChildNodeType();
                            parentNodeId = approFlowNotifierVO.getId();
                            newParentNodeId = flowNotifierVO.getId();
                            break;
                        default:
                            log.error("暂不支持的节点类型{}", childNodeType);
                            notLastNode = false;
                            break;
                    }
                }
                // 审批流程抄送
                List<ApproFlowProcessDO> approFlowProcessDOList
                        = flowProcessService.selectByFlowId(flowId, tokenUser.getSiteId());
                if (!CollectionUtils.isEmpty(approFlowProcessDOList)) {
                    for (ApproFlowProcessDO approFlowProcessDO : approFlowProcessDOList) {
                        ApproFlowProcessAddDTO approFlowNotifierAddDTO = approFlowProcessDO.to(ApproFlowProcessAddDTO.class);
                        approFlowNotifierAddDTO.setFlowId(approFlowVO.getId());
                        if (ProcessType.TYPE_FLOW.getValue().equals(approFlowNotifierAddDTO.getType())) {
                            for (String key : nodeMap.keySet()) {
                                approFlowNotifierAddDTO.setFlowNodeId(approFlowNotifierAddDTO.getFlowNodeId().replace(key, nodeMap.get(key)));
                            }
                        } else if (ProcessType.TYPE_LOCK.getValue().equals(approFlowNotifierAddDTO.getType())) {
                            approFlowNotifierAddDTO.setFlowBlockId(nodeMap.get(approFlowNotifierAddDTO.getFlowBlockId()));
                        } else {
                            throw new ParamException("审批流程抄送类型有误，请联系管理员");
                        }
                        flowProcessService.save(approFlowNotifierAddDTO, tokenUser);
                    }
                }
                // 审批高级设置下发
                ApproFlowSettingVO approFlowSettingVO = approFlowSettingService.findFlowSettingByFlowId(
                        flowId, siteId);
                if (approFlowSettingVO != null) {
                    ApproFlowSettingAddDTO approFlowSettingAddDTO = approFlowSettingVO.to(ApproFlowSettingAddDTO.class);
                    approFlowSettingAddDTO.setFlowId(approFlowVO.getId());
                    approFlowSettingService.save(approFlowSettingAddDTO, tokenUser);
                }
            }
        }
        return approFlowVOList;
    }

    @Override
    public List<ApproFlowListVO> selectAllFlowsByCompany(String companyId, String siteId) {
        return approFlowService.listAllFlowsByCompany(companyId, siteId);
    }

    @Override
    public List<UserApproNodeInfoVO> listUserApprovesWaitApproveByMe(UserFlowApproQuery userFlowApproQuery,
                                                                     TokenUser tokenUser) {
        userFlowApproQuery.setModuleType(HR_SUBJECT_TYPE);
        userFlowApproQuery.setHasAppro(false);
        return userApproService.listUserApprosApproverByMe(userFlowApproQuery, tokenUser);
    }

    @Override
    public List<UserApproNodeInfoVO> listUserApprovesFinishedApproveByMe(UserFlowApproQuery userFlowApproQuery,
                                                                         TokenUser tokenUser) {
        userFlowApproQuery.setModuleType(HR_SUBJECT_TYPE);
        userFlowApproQuery.setHasAppro(true);
        return userApproService.listUserApprosApproverByMe(userFlowApproQuery, tokenUser);
    }

    @Override
    public List<UserApproNodeInfoVO> listUserPassedApprovesApproveByMe(UserFlowApproQuery userFlowApproQuery,
                                                                       TokenUser tokenUser) {
        userFlowApproQuery.setModuleType(HR_SUBJECT_TYPE);
        userFlowApproQuery.setHasAppro(true);
        userFlowApproQuery.setStatus(2);
        return userApproService.listUserApprosApproverByMe(userFlowApproQuery, tokenUser);
    }

    @Override
    public List<UserApproNodeInfoVO> listUserRejectedApprovesApproveByMe(UserFlowApproQuery userFlowApproQuery,
                                                                         TokenUser tokenUser) {
        userFlowApproQuery.setModuleType(HR_SUBJECT_TYPE);
        userFlowApproQuery.setHasAppro(true);
        userFlowApproQuery.setStatus(3);
        return userApproService.listUserApprosApproverByMe(userFlowApproQuery, tokenUser);
    }

    @Override
    public List<UserApproNodeInfoVO> listPassedApproves(UserFlowApproQuery userFlowApproQuery, TokenUser tokenUser) {
        userFlowApproQuery.setModuleType(HR_SUBJECT_TYPE);
        userFlowApproQuery.setHasAppro(true);
        userFlowApproQuery.setStatus(2);
        List<UserApproNodeInfoVO> approNodeInfoVOS = userApproService.listUserApprosApproverByMe(userFlowApproQuery, tokenUser);
        if (CollectionUtils.isEmpty(approNodeInfoVOS)) {
            return new ArrayList<>();
        }
        // 审批状态:0:待审批1:审批中2:通过3:驳回4:撤回
        // 过滤掉总审批状态不是通过的
        approNodeInfoVOS.removeIf(s -> 2 != s.getApproStatu());
        return approNodeInfoVOS;
    }

    @Override
    public List<UserApproNodeInfoVO> listRejectedApproves(UserFlowApproQuery userFlowApproQuery, TokenUser tokenUser) {
        userFlowApproQuery.setModuleType(HR_SUBJECT_TYPE);
        userFlowApproQuery.setHasAppro(true);
        userFlowApproQuery.setStatus(3);
        List<UserApproNodeInfoVO> approNodeInfoVOS = userApproService.listUserApprosApproverByMe(userFlowApproQuery, tokenUser);
        if (CollectionUtils.isEmpty(approNodeInfoVOS)) {
            return new ArrayList<>();
        }
        // 审批状态:0:待审批1:审批中2:通过3:驳回4:撤回
        // 过滤掉总审批状态不是通过的
        approNodeInfoVOS.removeIf(s -> 3 != s.getApproStatu());
        return approNodeInfoVOS;
    }

    @Override
    public List<UserApproListVO> listUserApprosCreateByMe(UserFlowApproQuery userFlowApproQuery,
                                                          TokenUser tokenUser) {
        userFlowApproQuery.setModuleType(HR_SUBJECT_TYPE);
        return userApproService.listUserApprosCreateByMe(userFlowApproQuery, tokenUser);
    }

    @Override
    public List<UserApproNodeInfoVO> listUserApprosNotifierMe(UserFlowApproQuery userFlowApproQuery,
                                                              TokenUser tokenUser) {
        userFlowApproQuery.setModuleType(HR_SUBJECT_TYPE);
        return userApproService.listUserApprosNotifierMe(userFlowApproQuery, tokenUser);
    }

}
